package cn.com.gome.commons.qcode.utils;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
import com.jcraft.jsch.SftpProgressMonitor;

/**
 * SFTP工具类
 * 
 */
public class SFTPUtils {

	private static final String CHL_MODE_SFTP = "sftp";
	private static final String UTF8 = "UTF-8";
	
	
	
	/**
	 * 
	* @Title: getSession 
	* @Description: 获取session
	* @param hostname IP
	* @param port 端口
	* @param username 用户名
	* @param password 密码
	* @param privateKey 密钥文件路径(可传null或空字符串)
	* @param passphrase 密钥口令(可传null或空字符串)
	* @param timeout 超时时间
	* @return
	* @throws JSchException  
	* @return Session    返回类型 
	* @throws
	 */
	public static Session getSession(String hostname, int port, String username, String password, String privateKey, String passphrase, int timeout)
			throws JSchException {
		Session session = null;
		JSch jsch = new JSch();
		
		if (privateKey != null && !"".equals(privateKey)) {
			// 使用密钥验证方式，密钥可以使有口令的密钥，也可以是没有口令的密钥
			if (passphrase != null && !"".equals(passphrase)) {
				jsch.addIdentity(privateKey, passphrase);
			} else {
				jsch.addIdentity(privateKey);
			}
		}

		if (0 >= port) {
			session = jsch.getSession(username, hostname);
		} else {
			session = jsch.getSession(username, hostname, port);
		}
		
		Properties sshConfig = new Properties();
		sshConfig.setProperty("StrictHostKeyChecking", "no");
		session.setConfig(sshConfig);
		session.setPassword(password);
		session.setTimeout(timeout);
		session.connect();
		
		return session;
	}

	/**
	 * 
	* @Title: connect 
	* @Description: 获取sftp链接
	* @param hostname IP
	* @param port 端口
	* @param username 用户名
	* @param password 密码
	* @param privateKey 密钥文件路径(可传null或空字符串)
	* @param passphrase 密钥口令(可传null或空字符串)
	* @param timeout 超时时间
	* @return  
	* @return ChannelSftp    返回类型 
	* @throws
	 */
	public static ChannelSftp connect(String hostname, int port, String username, String password, String privateKey, String passphrase, int timeout) {
		Session session = null;
		ChannelSftp chlSftp = null;
		try {
			session = getSession(hostname, port, username, password, privateKey, passphrase, timeout);
			if (!session.isConnected()) {
                session.connect();
            }
		
			chlSftp = (ChannelSftp) session.openChannel(CHL_MODE_SFTP);
			chlSftp.connect();
			chlSftp.setFilenameEncoding(UTF8);
		} catch (JSchException e) {
			System.out.println("连接sftp服务器异常，异常信息："+e);
			releaseAll(chlSftp);
			return null;
		} catch (Exception e) {
			System.out.println("连接sftp服务器异常，异常信息："+e);
			return null;
		}
		return chlSftp;
	}

	public static final void releaseAll(ChannelSftp channelSftp) {
		try {
			if (channelSftp != null) {
				Session sshSession=channelSftp.getSession();
				releaseChlSftp(channelSftp);
				releaseSession(sshSession);
			}
		} catch (JSchException e) {
			System.out.println("释放sftp服务器链接异常，异常信息："+e);
		}
		
	}

	/**
	 * release session resource
	 * 
	 * @param session
	 */
	public static final void releaseSession(Session session) {
		if (null == session || !session.isConnected()) {
			return;
		} else {
			try {
				session.disconnect();
			} catch (Exception e) {
				System.out.println("释放session异常，异常信息："+e);
			} finally {
				session = null;
			}
		}
	}

	/**
	 * release Sftp Channel resource
	 * 
	 * @param chlSftp
	 */
	public static final void releaseChlSftp(ChannelSftp chlSftp) {
		if (null == chlSftp || !chlSftp.isConnected()) {
			return;
		} else {
			try {
				chlSftp.disconnect();
			} catch (Exception e) {
				System.out.println("释放ChannelSftp异常，异常信息："+e);
			} finally {
				chlSftp = null;
			}
		}
	}

	/**
	 * 
	 * @param src
	 *            源文件名
	 * @param dst
	 *            目的文件名
	 * @param monitor
	 * @param mode
	 *            = ChannelSftp.OVERWRITE、ChannelSftp.RESUME、ChannelSftp.APPEND
	 * @throws Exception
	 */
	public static void put(String src, String dst, SftpProgressMonitor monitor,
			int mode, ChannelSftp chlSftp) throws Exception {
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
			
		}
		if (null == src) {
			System.out.println(" upload null to sftp server is forbidden.");
			throw new Exception(" upload null to sftp server is forbidden.");
		}
		try {
			chlSftp.put(src, dst, monitor, mode);
		} catch (Exception e) {
			System.out.println("上传文件失败："+e);
			throw e;
		}
	}

	/**
	 * 以 OVERWRITE 方式写上传文件
	 * 
	 * @param src
	 *            源文件名
	 * @param dst
	 *            目的文件名
	 * @throws Exception
	 */
	public static void put(String src, String dst, ChannelSftp chlSftp) throws Exception {
		put(src, dst, null, ChannelSftp.OVERWRITE,chlSftp);
	}

	/**
	 * 
	 * @param src
	 *            源文件名
	 * @param dst
	 *            目的文件名
	 * @param mode
	 *            = ChannelSftp.OVERWRITE、ChannelSftp.RESUME、ChannelSftp.APPEND
	 * @throws Exception
	 */
	public static void put(String src, String dst, int mode, ChannelSftp chlSftp) throws Exception {
		put(src, dst, null, mode,chlSftp);
	}

	public static  void put(String src, String dst, SftpProgressMonitor monitor, ChannelSftp chlSftp)
			throws Exception {
		put(src, dst, monitor, ChannelSftp.OVERWRITE,chlSftp);
	}

	/**
	 * 
	 * @param src
	 *            输入流
	 * @param dst
	 *            目的文件
	 * @param monitor
	 * @param mode
	 *            = ChannelSftp.OVERWRITE、ChannelSftp.RESUME、ChannelSftp.APPEND
	 * @throws Exception
	 */
	public static void put(InputStream src, String dst, SftpProgressMonitor monitor,
			int mode, ChannelSftp chlSftp) throws Exception {
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
		}
		if (null == src) {
			System.out.println(" upload null to sftp server is forbidden.");
			throw new Exception(" upload null to sftp server is forbidden.");
		}
		try {
			chlSftp.put(src, dst, monitor, mode);
		} catch (Exception e) {
			System.out.println("上传文件失败："+e);
			throw e;
		}
	}

	/**
	 * 以 OVERWRITE 方式上传文件
	 * 
	 * @param src
	 *            输入流
	 * @param dst
	 * @throws Exception
	 */
	public static void put(InputStream src, String dst, ChannelSftp chlSftp) throws Exception {
		put(src, dst, null, ChannelSftp.OVERWRITE,chlSftp);
	}

	public static void put(InputStream src, String dst, int mode, ChannelSftp chlSftp) throws Exception {
		put(src, dst, null, mode,chlSftp);
	}

	public static void put(InputStream src, String dst, SftpProgressMonitor monitor, ChannelSftp chlSftp)
			throws Exception {
		put(src, dst, monitor, ChannelSftp.OVERWRITE,chlSftp);
	}

	/**
	 * 
	 * @param dst
	 *            目的文件名
	 * @param monitor
	 * @param mode
	 * @param offset
	 * @return 输出流
	 * @throws SftpException
	 */
	public static OutputStream put(String dst, final SftpProgressMonitor monitor,
			final int mode, long offset, ChannelSftp chlSftp) throws SftpException {
		OutputStream os = null;
		try {
			os = chlSftp.put(dst, monitor, mode, offset);
		} catch (SftpException e) {
			System.out.println("上传文件失败："+e);
			throw e;
		}
		return os;
	}

	/**
	 * 
	 * @param dst
	 *            目的文件名
	 * @param monitor
	 * @param mode
	 * @return 输出流
	 * @throws SftpException
	 */
	public static OutputStream put(String dst, final SftpProgressMonitor monitor,
			final int mode, ChannelSftp chlSftp) throws SftpException {
		return put(dst, monitor, mode, 0,chlSftp);
	}

	/**
	 * 
	 * @param dst
	 *            目的文件名
	 * @param mode
	 * @return 输出流
	 * @throws SftpException
	 */
	public static OutputStream put(String dst, final int mode, ChannelSftp chlSftp) throws SftpException {
		return put(dst, (SftpProgressMonitor) null, mode,chlSftp);
	}

	/**
	 * 
	 * @param dst
	 *            目的文件名
	 * @return 输出流
	 * @throws SftpException
	 */
	public static OutputStream put(String dst, ChannelSftp chlSftp) throws SftpException {
		return put(dst, (SftpProgressMonitor) null, ChannelSftp.OVERWRITE,chlSftp);
	}

	/**
	 * 
	 * @param src
	 *            源文件名
	 * @param dst
	 *            目的文件名
	 * @param monitor
	 * @param mode
	 * @throws Exception
	 */
	public static void get(String src, String dst, SftpProgressMonitor monitor,
			int mode, ChannelSftp chlSftp) throws Exception {
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
		}
		if (null == src) {
			System.out.println(" upload null to sftp server is forbidden.");
			throw new Exception(" upload null to sftp server is forbidden.");
		}
		try {
			chlSftp.get(src, dst, monitor, mode);
		} catch (Exception e) {
			System.out.println("获取文件失败："+e);
			throw e;
		}
	}

	public static void get(String src, String dst, ChannelSftp chlSftp) throws Exception {
		get(src, dst, null, ChannelSftp.OVERWRITE,chlSftp);
	}

	public static void get(String src, String dst, SftpProgressMonitor monitor, ChannelSftp chlSftp)
			throws Exception {
		get(src, dst, monitor, ChannelSftp.OVERWRITE,chlSftp);
	}

	/**
	 * 
	* @Title: get 
	* @Description: 
	* @param src
	* @param dst
	* @param monitor
	* @param mode
	* @param skip
	* @throws Exception  
	* @return void    返回类型 
	* @throws
	 */
	public static void get(String src, OutputStream dst, SftpProgressMonitor monitor,
			int mode, long skip, ChannelSftp chlSftp) throws Exception {
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
		}
		if (null == src) {
			System.out.println(" upload null to sftp server is forbidden.");
			throw new Exception(" upload null to sftp server is forbidden.");
		}
		try {
			chlSftp.get(src, dst, monitor, mode, skip);
		} catch (Exception e) {
			System.out.println("获取文件失败："+e);
			throw e;
		}
	}

	/**
	 * 
	 * @param chlSftp
	 * @param src
	 * @param dst
	 * @throws Exception
	 */
	public static void get(String src, OutputStream dst, ChannelSftp chlSftp) throws Exception {
		get(src, dst, null, ChannelSftp.OVERWRITE, 0,chlSftp);
	}

	/**
	 * 
	 * @param chlSftp
	 * @param src
	 * @param dst
	 * @param monitor
	 * @throws Exception
	 */
	public static void get(String src, OutputStream dst, SftpProgressMonitor monitor, ChannelSftp chlSftp)
			throws Exception {
		get(src, dst, monitor, ChannelSftp.OVERWRITE, 0,chlSftp);
	}

	/**
	 * @param src
	 *            文件名
	 * @param monitor
	 * @param skip
	 * @return 输入流
	 * @throws Exception
	 */
	public static InputStream get(String src, final SftpProgressMonitor monitor,
			final long skip, ChannelSftp chlSftp) throws Exception {
		InputStream inStream = null;
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
		}
		if (null == src) {
			System.out.println(" upload null to sftp server is forbidden.");
			throw new Exception(" upload null to sftp server is forbidden.");
		}
		try {
			inStream = chlSftp.get(src, monitor, skip);
		} catch (Exception e) {
			System.out.println("获取文件失败："+e);
			throw e;
		}
		return inStream;
	}

	/**
	 * 
	 * @param src
	 *            文件名
	 * @return 输入流
	 * @throws Exception
	 */
	public static InputStream get(String src, ChannelSftp chlSftp) throws Exception {
		return get(src, null, 0L,chlSftp);
	}

	/**
	 * 
	 * @param src
	 *            文件名
	 * @param monitor
	 * @return 输入流
	 * @throws Exception
	 */
	public static InputStream get(String src, SftpProgressMonitor monitor, ChannelSftp chlSftp)
			throws Exception {
		return get(src, monitor, 0L,chlSftp);
	}

	/**
	 * 改变本地路径[客户端]
	 * 
	 * @param chlSftp
	 * @param path
	 * @throws Exception
	 */
	public static void lcd(String path, ChannelSftp chlSftp) throws Exception {
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
		}
		try {
			chlSftp.lcd(path);
		} catch (Exception e) {
			System.out.println("改变本地路径[客户端]失败："+e);
			throw e;
		}
	}

	/**
	 * 改变路径[服务器]
	 * 
	 * @param chlSftp
	 * @param path
	 * @throws Exception
	 */
	public static void cd(String path, ChannelSftp chlSftp) throws Exception {
		if (null == chlSftp || !chlSftp.isConnected()) {
			System.out.println("sftp Channel invalid.");
			throw new Exception("sftp Channel invalid.");
		}
		try {
			chlSftp.cd(path);
		} catch (Exception e) {
			System.out.println("改变路径[服务器]失败："+e);
			throw e;
		}
	}

	/**
	 * 过滤方式取path下面的文件，仅取文件后缀为suffix，不取文件夹
	 * 
	 * @param path
	 * @param suffix
	 * @return 文件名集合
	 * @throws SftpException
	 */
	public static Vector ls(String path, String suffix, ChannelSftp chlSftp) throws SftpException {
		Vector filses = chlSftp.ls(path);
		Vector myFilses = new Vector();
		Iterator it = filses.iterator();
		while (it.hasNext()) {
			LsEntry lsentry = (LsEntry) it.next();
			if (lsentry.getFilename().toLowerCase().endsWith(
					suffix.toLowerCase())
					&& lsentry.getLongname().startsWith("-")) {
				myFilses.add(lsentry);
			}
		}
		return myFilses;
	}

	/**
	 * 过滤方式取path下面的文件，仅取文件后缀为suffix，不取文件夹
	 * 
	 * @param path
	 * @param suffix
	 * @return 文件夹下是否有文件
	 * @throws SftpException
	 */
	public static boolean lsFolder(String path, String folderName, ChannelSftp chlSftp)
			throws SftpException {
		Vector filses = chlSftp.ls(path);
		Iterator it = filses.iterator();
		while (it.hasNext()) {
			LsEntry lsentry = (LsEntry) it.next();
			if (lsentry.getLongname().startsWith("d")
					&& folderName.equals(lsentry.getFilename())) {
				return false;
			}
		}
		return true;
	}


	
	/**
	 * 上传文件
	 * 
	 * @param directory 上传的目录，如果目录不存在，则自动创建
	 * @param uploadFile 要上传的文件
	 */
	public static boolean upload(String directory, String uploadFile, ChannelSftp chlSftp) {
		boolean result = false;
		try {
			/**进入文件夹 并上传文件，并修改结果为true*/
			if(mkdirs(directory,chlSftp)){
				chlSftp.cd(directory);
				File file = new File(uploadFile);
				chlSftp.put(new FileInputStream(file), file.getName());
				result = true;	
			}
		} catch (Exception e) {
			System.out.println("上传文件失败："+e);
		}
		return result;
	}
	/**
	 * 上传文件
	 * 
	 * @param dst  目的文件名
	 * @param is   输入流
	 * @param mode 上传的方式
	 * @throws IOException 
	 */
	public static boolean upload(String dst, InputStream is, ChannelSftp chlSftp,int mode) throws IOException {
		boolean result = false;
		try {
			/**进入文件夹 并上传文件，并修改结果为true*/
				chlSftp.put(is,dst,mode);
				result = true;	
		} catch (Exception e) {
			System.out.println("上传文件失败："+e);
		}finally{
			if(is!=null){
				is.close();
			}
			releaseAll(chlSftp);
		}
		return result;
	}
	/**
	 * 递归创建文件夹
	 * @param directory
	 * @return
	 * @throws SftpException
	 */
	public static boolean mkdirs(String directory, ChannelSftp chlSftp){
		boolean result = false;
		/** 判断文件是否已经存在*/
		try {
			System.out.println("-----目录路径： " + directory);
			SftpATTRS sftpAttrs = chlSftp.lstat(directory);
			/**  判断是否是文件类型 */
			if(sftpAttrs.isDir()){
				return true;
			}
		} catch (SftpException e1) {
			e1.printStackTrace();
			//不存在的情况
			//查看父级是否存在
			//(确认父级存在)父级不存在->递归调用父级
			try {
				directory = directory.replaceAll("\\\\", "/");
				System.out.println("-----目录路径： " + directory);
				SftpATTRS sftpAttrs = chlSftp.lstat(directory.substring(0,directory.lastIndexOf("/")));
				/**  判断是否是文件类型 */
				if(!sftpAttrs.isDir()){
					//不操作
					return false;
				}
			}catch(Exception e){
				e.printStackTrace();
				/**递归生成父级*/
				System.out.println("-----目录路径： " + directory);
				boolean res_f = mkdirs(directory.substring(0,directory.lastIndexOf("/")),chlSftp);
				if(!res_f){
					//父级没有创建成功，
					result = false;
					return result;
				}
			}
			//父级存在->直接创建
			try{
				System.out.println("-----目录路径： " + directory);
				chlSftp.mkdir(directory);
				result = true;
			}catch(Exception e){
				e.printStackTrace();
				result = false;
			}
		}
		return result;
	}
	
	/**
	 * 删除文件
	 * 
	 * @param directory
	 *            要删除文件所在目录
	 * @param deleteFile
	 *            要删除的文件
	 * @param sftp
	 */
	public static void delete(String directory, String deleteFile, ChannelSftp sftp) {
		try {
			sftp.cd(directory);
			sftp.rm(deleteFile);
		} catch (Exception e) {
			System.out.println("删除文件异常："+e);
		}
	}
	
	/**
	 * 
	* @Title: readFileFromSFTP 
	* @Description: 读取文件内容 
	* @param filePath 文件路径
	* @param fileName  文件名字
	* @param sftp 
	* @return  
	* @return List<String>    返回类型 
	* @throws
	 */
	public static List<String> readFileFromSFTP(String filePath, String fileName,ChannelSftp sftp){
		InputStream in = null;
		List<String> result = new ArrayList<String>();
		try {
			if (null == sftp || !sftp.isConnected()) {
				System.out.println("sftp Channel invalid.");
				throw new Exception("sftp Channel invalid.");
			}
			
			if((null != filePath) && (!"".equals(filePath))){
				sftp.cd(filePath);
			}
			in=sftp.get(fileName);
			
			if (null != in) {     
			    LineNumberReader lineInputStream = new LineNumberReader(new InputStreamReader(in));
		    	String data = null;  
		    	try {  
		    		while ((data = lineInputStream.readLine()) != null) {  
		    			if(data!=null&&!data.trim().equals("")){
		    				result.add(data);
		    			}
		    		}  
		    		in.close(); 
		    	} catch (IOException e) {  
		    			System.out.println("文件读取错误："+e);
		    	}
		    }else{
		    	System.out.println(" 未获取到文件 "+fileName+" 内容，请核查文件的有效性。");
		    }  
		} catch (Exception e) {
			System.out.println(" 未获取到文件 "+fileName+" 内容，请核查文件的有效性。"+e);
		}
		return result;
	}
	
	/**
	 * 下载文件
	 * 
	 * @param directory
	 *            下载目录
	 * @param downloadFile
	 *            下载的文件
	 * @param saveFile
	 *            存在本地的路径
	 * @param sftp
	 */
	public  static boolean download(String directory, String downloadFile,
			String saveFile, ChannelSftp sftp) {
		boolean result = false;
		FileOutputStream fileOutputStream=null;
		File file =null;
		try {
			file = new File(saveFile);
			fileOutputStream=new FileOutputStream(file);
			sftp.cd(directory);
			sftp.get(downloadFile,fileOutputStream );
			result = true;
		} catch (Exception e) {
			System.out.println("下载文件失败："+e);
		}finally{
           if(fileOutputStream!=null){
        	   try {
				fileOutputStream.close();
			} catch (IOException e) {
				System.out.println("释放链接失败："+e);
			}
           }
		}
		return result;
	}
	
	/**
	 * 
	* @Title: listFiles 
	* @Description: 获取指定目录下的文件列表
	* @param remotePath
	* @param sftp
	* @return  
	* @return Vector<LsEntry>    返回类型 
	* @throws
	 */
	public static Vector<LsEntry> listFiles(String remotePath, ChannelSftp sftp) {
		Vector<LsEntry> vector = new Vector<ChannelSftp.LsEntry>();
		try {
			if (null == sftp || !sftp.isConnected()) {
				System.out.println("sftp Channel invalid.");
				throw new Exception("sftp Channel invalid.");
			}
			if (null == remotePath) {
				System.out.println(" remotePath null to sftp server is forbidden.");
				throw new Exception(" remotePath null to sftp server is forbidden.");
			}
			System.out.println("获取文件路径：remotePath=" + remotePath);
			
			sftp.cd(remotePath);
			vector = sftp.ls(remotePath);
		} catch (Exception e) {
			System.out.println("没有找到文件: remotePath:" + remotePath+e);
		}

		return vector;
	}
	
	
	
	/**
	 * 
	* @Title: moveFileToDst 
	* @Description: 复制文件到指定目录中
	* @param sftp
	* @param src  源路径（绝对路径）
	* @param fileName  文件名
	* @param dst  目标路径（绝对路径）
	* @return void    返回类型 
	* @throws
	 */
	public static void moveFileToDst(ChannelSftp sftp, String src, String fileName, String dst) {
		System.out.println("moveFileToDst src: "+src+" -----fileName: " + fileName + " -----dst: " + dst);
		try {
			if (null == sftp || !sftp.isConnected()) {
				System.out.println("sftp Channel invalid.");
				throw new Exception("sftp Channel invalid.");
			}
			
			if ((src != null) && (!"".equals(src))
					&& (null != fileName) && (!"".equals(fileName)) 
					&& (null != dst) && (!"".equals(dst))) {
				sftp.cd("/" + src);
				System.out.println("start to move file "+ fileName + " from " + src + " to " + dst);
				if(mkdirs(dst,sftp)){
					sftp.rename(src + fileName, dst + "/" + fileName);
				}
			}
		}
		catch (Exception e) {
			System.out.println("move file "+ fileName + " error"+e );
		}
	}
	
	public static void main(String[] args) throws SftpException{
		//连接sftp
		ChannelSftp sftp = SFTPUtils.connect("10.126.53.115", 22, "GomeMFBSIT", "GomeMFBSIT",null,null,0);
		//复制文件
		SFTPUtils.moveFileToDst(sftp, "/data/MFB_SIT/", "GM02_file_index_20141029_1.md5", "/data/MFB_SIT/MFB_ttt/20151233");
		//释放连接
		SFTPUtils.releaseAll(sftp);
	}
}
